﻿__author__ = 'YT'

import pygame as pg
from .. import tool
from .. import constants as c
from .. import AStarSearch
from . import map

class FireBall():
    def __init__(self, x, y, enemy, hurt):
        # 初始化火球类
        frame_rect = (0,0,14,14)
        # 获取火球的图片
        self.image = tool.get_image(tool.GFX[c.FIREBALL], *frame_rect, c.BLACK, c.SIZE_MULTIPLIER)
        self.rect = self.image.get_rect()
        self.rect.centerx = x
        self.rect.centery = y
        # 判断火球是否为敌人火球
        self.enemy = enemy
        self.hurt = hurt
        self.done = False
        # 计算火球的初速度
        self.calVelocity()
    
    def calVelocity(self):
        # 计算敌人与自己的水平距离
        dis_x = self.enemy.rect.centerx - self.rect.centerx
        # 计算敌人与自己的垂直距离
        dis_y = self.enemy.rect.centery - self.rect.centery
        # 计算敌人与自己的距离
        distance = (dis_x ** 2 + dis_y ** 2) ** 0.5
        # 计算水平速度
        self.x_vel = (dis_x * 10)/distance
        # 计算垂直速度
        self.y_vel = (dis_y * 10)/distance
        
    def update(self):
        # 移动子弹
        self.rect.x += self.x_vel
        self.rect.y += self.y_vel
        # 如果子弹和敌人的距离小于25，则敌人受伤，子弹消失
        if abs(self.rect.x - self.enemy.rect.x) + abs(self.rect.y - self.enemy.rect.y) < 25:
            self.enemy.setHurt(self.hurt)
            self.done = True
    
    def draw(self, surface):
        # 把图片image绘制到指定的表面surface上，并把它的矩形位置设置为rect
        surface.blit(self.image, self.rect)

# 定义EntityAttr类
class EntityAttr():
    # 初始化函数
    def __init__(self, data):
        # 获取data中的属性值
        self.max_health = data[c.ATTR_HEALTH]
        self.range = data[c.ATTR_RANGE]
        self.damage = data[c.ATTR_DAMAGE]
        self.attack = data[c.ATTR_ATTACK]
        self.defense = data[c.ATTR_DEFENSE]
        self.speed = data[c.ATTR_SPEED]
        # 根据data中的值判断是否为远程攻击
        if data[c.ATTR_REMOTE] == 0:
            self.remote = False
        else:
            self.remote = True
    
    def getHurt(self, enemy_attr):
        # 计算攻击和防御的差值
        offset = 0
        if self.attack > enemy_attr.defense:
            # 如果攻击力高于敌人的防御力，计算差值的一部分作为伤害修正值
            offset = (self.attack - enemy_attr.defense) * 0.05
        elif self.attack < enemy_attr.defense:
            # 如果攻击力低于敌人的防御力，计算差值的一部分作为伤害修正值
            offset = (self.attack - enemy_attr.defense) * 0.025
    
        # 计算最终伤害值
        hurt = int(self.damage * (1 + offset))
        return hurt


class Entity():
    def __init__(self, group, sheet, map_x, map_y, data):
        '''初始化实体类'''
        self.group = group
        self.group_id = group.group_id
        self.map_x = map_x
        self.map_y = map_y
        self.frames = []
        self.frame_index = 0
        self.loadFrames(sheet)
        self.image = self.frames[self.frame_index]
        self.rect = self.image.get_rect()
        self.rect.x, self.rect.y = self.getRectPos(map_x, map_y)
        
        self.attr = EntityAttr(data)
        self.health = self.attr.max_health
        self.weapon = None
        self.enemy = None
        self.state = c.IDLE
        self.animate_timer = 0.0
        self.current_time = 0.0
        self.move_speed = c.MOVE_SPEED
    
    def getRectPos(self, map_x, map_y):
        # 如果地图是六边形
        if c.MAP_HEXAGON:
            # 获取六边形地图的坐标
            base_x, base_y = tool.getHexMapPos(map_x, map_y)
            # 返回矩形坐标
            return (base_x + 4, base_y + 6)
        else:
            # 返回矩形坐标
            return(map_x * c.REC_SIZE + 5, map_y * c.REC_SIZE + 8)

    def getRecIndex(self, x, y):
        # 如果地图是六边形
        if c.MAP_HEXAGON:
            # x坐标加上六边形x轴的一半减去4，y坐标加上六边形y轴的一半减去6
            x += c.HEX_X_SIZE // 2 - 4
            y += c.HEX_Y_SIZE // 2 - 6
            # 获取六边形地图的坐标
            map_x, map_y = tool.getHexMapIndex(x, y)
        # 如果地图是矩形
        else:
            # 获取矩形地图的坐标
            map_x, map_y = (x//c.REC_SIZE, y//c.REC_SIZE)
        # 返回地图坐标
        return (map_x, map_y)

    def loadFrames(self, sheet):
        #frame_rect_list = [(64, 0, 32, 32), (96, 0, 32, 32)]
        #for frame_rect in frame_rect_list:
        #    self.frames.append(tool.get_image(sheet, *frame_rect, 
        #                    c.BLACK, c.SIZE_MULTIPLIER))
        # 加载帧，frame_rect_list是一个列表，里面存储了每一帧的坐标和大小
        frame_rect_list = [(0, 0, 32, 32), (0, 0, 32, 32)]
        # 遍历frame_rect_list，获取每一帧的坐标和大小
        for frame_rect in frame_rect_list:
            # 使用tool.get_image()函数从sheet中获取每一帧，并将其添加到self.frames列表中
            self.frames.append(tool.get_image(sheet, *frame_rect, 
                            c.BLACK, c.SIZE_MULTIPLIER))
        
    def setDestination(self, map_x, map_y, enemy=None):
        # 设置目标位置
        self.dest_x, self.dest_y = self.getRectPos(map_x, map_y)
        # 当前位置
        self.next_x, self.next_y = self.rect.x, self.rect.y
        # 敌人
        self.enemy = enemy
        # 状态
        self.state = c.WALK
    
    def setTarget(self, enemy):
        # 设置敌人
        self.enemy = enemy
        # 设置状态为攻击
        self.state = c.ATTACK

    def getHealthRatio(self):
        # 如果角色的生命值大于0
        if self.health > 0:
            # 计算角色的生命值百分比
            return self.health / self.attr.max_health
        else:
            # 如果角色的生命值小于等于0，则生命值百分比为0
            return 0
    
    def isDead(self):
        # 判断角色是否死亡
        return self.health <= 0
    
    def isRemote(self):
        # 返回self.attr的远程属性
        return self.attr.remote

    def inRange(self, map, map_x, map_y):
        # 判断指定坐标是否在范围内
        location = AStarSearch.AStarSearch(map, (self.map_x, self.map_y), (map_x, map_y))
        # 如果指定坐标可达
        if location is not None:
            # 获取第一步和距离
            _, _, distance = AStarSearch.getFirstStepAndDistance(location)
            # 如果距离小于等于攻击范围
            if distance <= self.attr.range:
                return True
        # 否则不在范围内
        return False
    
    def putHurt(self, enemy):
        # 计算对敌人的伤害
        hurt = self.attr.getHurt(enemy.attr)
        # 敌人受到伤害
        enemy.setHurt(hurt)

    def setHurt(self, damage):
        # 减少玩家的生命值
        self.health -= damage
        # 如果玩家死亡，则从组中移除该玩家
        if self.isDead():
            self.group.removeEntity(self)
    
    def shoot(self, enemy):
        # 计算伤害
        hurt = self.attr.getHurt(enemy.attr)
        # 发射火球
        self.weapon = FireBall(*self.rect.center, self.enemy, hurt)
     
    def walkToDestination(self, map):
        # 如果当前位置等于下一个位置，则进行路径搜索
        if self.rect.x == self.next_x and self.rect.y == self.next_y:
            # 获取当前位置和目标位置的索引
            source = self.getRecIndex(self.rect.x, self.rect.y)
            dest = self.getRecIndex(self.dest_x, self.dest_y)
            # 进行A*搜索
            location = AStarSearch.AStarSearch(map, source, dest)
            # 如果搜索成功，则获取第一个点和距离
            if location is not None:
                map_x, map_y, _ = AStarSearch.getFirstStepAndDistance(location)
                # 获取下一个位置的坐标
                self.next_x, self.next_y = self.getRectPos(map_x, map_y)
            # 如果搜索失败，则进入 idle 状态
            else:
                self.state = c.IDLE

        # 如果地图是六边形，则 x 和 y 方向都要移动
        if c.MAP_HEXAGON and self.rect.x != self.next_x and self.rect.y != self.next_y:
            self.rect.x += self.move_speed if self.rect.x < self.next_x else -self.move_speed
            self.rect.y += self.move_speed if self.rect.y < self.next_y else -self.move_speed
        # 如果 x 方向不一致，则只移动 x 方向
        elif self.rect.x != self.next_x:
            self.rect.x += self.move_speed if self.rect.x < self.next_x else -self.move_speed
        # 如果 y 方向不一致，则只移动 y 方向
        elif self.rect.y != self.next_y:
            self.rect.y += self.move_speed if self.rect.y < self.next_y else -self.move_speed

    def update(self, game_info, map):
        # 更新当前时间
        self.current_time = game_info[c.CURRENT_TIME]
        # 如果是走路状态
        if self.state == c.WALK:
            # 如果间隔时间大于250毫秒
            if (self.current_time - self.animate_timer) > 250:
                # 如果当前帧为0
                if self.frame_index == 0:
                    # 当前帧变为1
                    self.frame_index = 1
                else:
                    # 当前帧变为0
                    self.frame_index = 0
                # 更新动画计时器
                self.animate_timer = self.current_time

            # 如果矩形x坐标不等于目标x坐标或者矩形y坐标不等于目标y坐标
            if self.rect.x != self.dest_x or self.rect.y != self.dest_y:
                # 走路到目的地
                self.walkToDestination(map)
            else:
                # 将当前位置的实体设置为None
                map.setEntity(self.map_x, self.map_y, None)
                # 获取目标位置的索引
                self.map_x, self.map_y = self.getRecIndex(self.dest_x, self.dest_y)
                # 将目标位置的实体设置为自身
                map.setEntity(self.map_x, self.map_y, self)
                # 如果敌人不存在
                if self.enemy is None:
                    # 状态变为空闲
                    self.state = c.IDLE
                else:
                    # 状态变为攻击
                    self.state = c.ATTACK
        # 如果是在攻击状态
        elif self.state == c.ATTACK:
            # 如果敌人是远程
            if self.attr.remote:
                # 如果武器不存在
                if self.weapon is None:
                    # 射击敌人
                    self.shoot(self.enemy)
                else:
                    # 更新武器
                    self.weapon.update()
                    # 如果武器已经使用完
                    if self.weapon.done:
                        # 将武器设置为None
                        self.weapon = None
                        # 将敌人设置为None
                        self.enemy = None
                        # 状态变为空闲
                        self.state = c.IDLE
            else:
                # 伤害敌人
                self.putHurt(self.enemy)
                # 将敌人设置为None
                self.enemy = None
                # 状态变为空闲
                self.state = c.IDLE

        # 如果是在空闲状态
        if self.state == c.IDLE:
            # 当前帧设置为0
            self.frame_index = 0

    def draw(self, surface):
        # 绘制精灵的图像
        self.image = self.frames[self.frame_index]
        surface.blit(self.image, self.rect)
        # 根据精灵的生命值计算宽度
        width = self.rect.width * self.getHealthRatio()
        height = 5

        # 设置字体
        font = pg.font.Font(None, 16)
        # 打印文本
        str_health = str(self.health) + str("/")  + str(self.attr.max_health)
        text = font.render(str(str_health), True, pg.Color('black'))

        surface.blit(text, dest=(self.rect.left, self.rect.top))

        # 绘制红色血条
        pg.draw.rect(surface, c.RED, pg.Rect(self.rect.left, self.rect.top - height - 1, width, height))

        # 如果精灵有武器，则绘制武器
        if self.weapon is not None:
            self.weapon.draw(surface)

class EntityGroup():
    def __init__(self, group_id):
        # 初始化实体组，group_id为实体组的id
        self.group = []
        self.group_id =  group_id
        self.entity_index = 0

    def createEntity(self, entity_list, map):
        #遍历实体列表
        for data in entity_list:
            #获取实体名称、地图坐标
            entity_name, map_x, map_y = data['name'], data['x'], data['y']
            #如果地图坐标x小于0，则取地图长度加上x
            if map_x < 0:
                map_x = c.GRID_X_LEN + map_x
            #如果地图坐标y小于0，则取地图长度加上y
            if map_y < 0:
                map_y = c.GRID_Y_LEN + map_y

            #创建实体
            entity = Entity(self, tool.GFX[entity_name], map_x, map_y, tool.ATTR[entity_name])
            #将实体添加到组中
            self.group.append(entity)
            #在地图上设置实体
            map.setEntity(map_x, map_y, entity)
    def removeEntity(self, entity):
        # 遍历group列表，查找entity的位置
        for i in range(len(self.group)):
            if self.group[i] == entity:
                # 如果entity_index大于entity的位置，则将其减1
                if (self.entity_index > i or
                    (self.entity_index >= len(self.group) - 1)):
                    self.entity_index -= 1
        # 从group列表中移除entity
        self.group.remove(entity)
    
    def isEmpty(self):
        # 如果group列表长度为0，则返回True
        if len(self.group) == 0:
            return True
        # 否则返回False
        return False

    def nextTurn(self):
        # 重置实体索引
        self.entity_index = 0

    def getActiveEntity(self):
        # 如果实体索引大于等于组的长度，则实体为None
        if self.entity_index >= len(self.group):
            entity = None
        # 否则实体为组的实体索引
        else:
            entity = self.group[self.entity_index]
        # 返回实体
        return entity

    def consumeEntity(self):
        # 消耗实体，实体索引加1
        self.entity_index += 1

    def update(self, game_info, map):
        #遍历self.group中的每一个entity
        for entity in self.group:
            #调用entity的update方法，传入game_info和map
            entity.update(game_info, map)

    def draw(self, surface):
        #遍历self.group中的每一个实体
        for entity in self.group:
            #调用实体对象的draw方法，将实体绘制到指定的表面
            entity.draw(surface)